package com.imut.lagain.service.impl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.imut.lagain.entity.PeriodRecord;
import com.imut.lagain.service.IPeriodPredictionService;
import com.imut.lagain.service.IPeriodRecordService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 生理期智能预测服务实现类
 */
@Service
@RequiredArgsConstructor
public class PeriodPredictionServiceImpl implements IPeriodPredictionService {
    private static final Logger log = LoggerFactory.getLogger(PeriodPredictionServiceImpl.class);

    
    private final IPeriodRecordService periodRecordService;
    private static final int DEFAULT_CYCLE_LENGTH = 28;
    private static final int DEFAULT_PERIOD_LENGTH = 5;
    private static final int OVULATION_DAY = 14;
    private static final int FERTILE_RANGE_BEFORE = 5;
    private static final int FERTILE_RANGE_AFTER = 1;
    
    @Override
    public LocalDate predictNextPeriodStart(Long userId) {
        List<PeriodRecord> records = periodRecordService.findRecentByUserId(userId, 6);
        
        if (records.isEmpty()) {
            return null;
        }
        
        PeriodRecord lastRecord = records.get(0);
        Double avgCycleLength = calculateAverageCycleLength(userId);
        
        if (avgCycleLength == null) {
            avgCycleLength = (double) DEFAULT_CYCLE_LENGTH;
        }
        
        return lastRecord.getStartDate().plusDays(avgCycleLength.longValue());
    }
    
    @Override
    public LocalDate predictOvulationDate(Long userId) {
        LocalDate nextPeriodStart = predictNextPeriodStart(userId);
        
        if (nextPeriodStart == null) {
            return null;
        }
        return nextPeriodStart.minusDays(OVULATION_DAY);
    }
    
    @Override
    public Map<String, LocalDate> predictFertilePeriod(Long userId) {
        LocalDate ovulationDate = predictOvulationDate(userId);
        
        if (ovulationDate == null) {
            return new HashMap<>();
        }
        
        Map<String, LocalDate> fertilePeriod = new HashMap<>();
        fertilePeriod.put("start", ovulationDate.minusDays(FERTILE_RANGE_BEFORE));
        fertilePeriod.put("end", ovulationDate.plusDays(FERTILE_RANGE_AFTER));
        fertilePeriod.put("ovulation", ovulationDate);
        
        return fertilePeriod;
    }
    
    @Override
    public Double calculateAverageCycleLength(Long userId) {
        List<PeriodRecord> records = periodRecordService.findRecentByUserId(userId, 6);
        
        if (records.size() < 2) {
            return null;
        }
        
        List<Long> cycleLengths = new ArrayList<>();
        
        for (int i = 0; i < records.size() - 1; i++) {
            LocalDate currentStart = records.get(i).getStartDate();
            LocalDate previousStart = records.get(i + 1).getStartDate();
            long cycleLength = ChronoUnit.DAYS.between(previousStart, currentStart);
            cycleLengths.add(cycleLength);
        }
        
        return cycleLengths.stream()
                .mapToLong(Long::longValue)
                .average()
                .orElse(DEFAULT_CYCLE_LENGTH);
    }
    
    @Override
    public Double calculateAveragePeriodLength(Long userId) {
        List<PeriodRecord> records = periodRecordService.findRecentByUserId(userId, 6);
        
        if (records.isEmpty()) {
            return null;
        }
        
        List<Long> periodLengths = records.stream()
                .filter(record -> record.getEndDate() != null)
                .map(record -> ChronoUnit.DAYS.between(record.getStartDate(), record.getEndDate()) + 1)
                .collect(Collectors.toList());
        
        if (periodLengths.isEmpty()) {
            return (double) DEFAULT_PERIOD_LENGTH;
        }
        
        return periodLengths.stream()
                .mapToLong(Long::longValue)
                .average()
                .orElse(DEFAULT_PERIOD_LENGTH);
    }
    
    @Override
    public Map<String, Object> analyzeSymptomPatterns(Long userId) {
        List<PeriodRecord> records = periodRecordService.findRecentByUserId(userId, 12);
        Map<String, Object> analysis = new HashMap<>();
        
        if (records.isEmpty()) {
            analysis.put("hasData", false);
            return analysis;
        }
        Map<String, Integer> symptomFrequency = new HashMap<>();
        Map<String, List<String>> phaseSymptoms = new HashMap<>();
        
        for (PeriodRecord record : records) {
            if (record.getSymptoms() != null) {
                String symptomsStr = record.getSymptoms().toString();
                if (symptomsStr.contains("cramps")) {
                    symptomFrequency.merge("cramps", 1, Integer::sum);
                }
                if (symptomsStr.contains("bloating")) {
                    symptomFrequency.merge("bloating", 1, Integer::sum);
                }
                if (symptomsStr.contains("headache")) {
                    symptomFrequency.merge("headache", 1, Integer::sum);
                }
                if (symptomsStr.contains("fatigue")) {
                    symptomFrequency.merge("fatigue", 1, Integer::sum);
                }
            }
        }
        
        analysis.put("hasData", true);
        analysis.put("symptomFrequency", symptomFrequency);
        analysis.put("totalRecords", records.size());
        analysis.put("mostCommonSymptom", getMostCommonSymptom(symptomFrequency));
        
        return analysis;
    }
    
    @Override
    public Map<String, Object> detectCycleAnomalies(Long userId) {
        List<PeriodRecord> records = periodRecordService.findRecentByUserId(userId, 6);
        Map<String, Object> anomalies = new HashMap<>();
        
        if (records.size() < 3) {
            anomalies.put("hasEnoughData", false);
            return anomalies;
        }
        
        Double avgCycleLength = calculateAverageCycleLength(userId);
        List<String> warnings = new ArrayList<>();
        if (avgCycleLength != null) {
            if (avgCycleLength < 21) {
                warnings.add("周期偏短，建议咨询医生");
            } else if (avgCycleLength > 35) {
                warnings.add("周期偏长，建议咨询医生");
            }
            List<Long> cycleLengths = new ArrayList<>();
            for (int i = 0; i < records.size() - 1; i++) {
                LocalDate currentStart = records.get(i).getStartDate();
                LocalDate previousStart = records.get(i + 1).getStartDate();
                long cycleLength = ChronoUnit.DAYS.between(previousStart, currentStart);
                cycleLengths.add(cycleLength);
            }
            double variance = cycleLengths.stream()
                    .mapToDouble(length -> Math.pow(length - avgCycleLength, 2))
                    .average()
                    .orElse(0.0);
            double stdDev = Math.sqrt(variance);
            
            if (stdDev > 7) {
                warnings.add("周期不规律，建议记录更多数据");
            }
        }
        
        anomalies.put("hasEnoughData", true);
        anomalies.put("warnings", warnings);
        anomalies.put("averageCycleLength", avgCycleLength);
        anomalies.put("isRegular", warnings.isEmpty());
        
        return anomalies;
    }
    
    @Override
    public List<Map<String, String>> generateHealthTips(Long userId, String currentPhase) {
        List<Map<String, String>> tips = new ArrayList<>();
        
        switch (currentPhase.toLowerCase()) {
            case "menstrual":
                tips.add(createTip("🌡️", "保暖", "注意腹部和腰部保暖，可以用热水袋缓解疼痛"));
                tips.add(createTip("🥛", "补铁", "多吃富含铁质的食物，如红肉、菠菜、豆类"));
                tips.add(createTip("😴", "休息", "保证充足睡眠，避免剧烈运动"));
                tips.add(createTip("🧘‍♀️", "放松", "可以做轻柔的瑜伽或冥想来缓解不适"));
                break;
            case "follicular":
                tips.add(createTip("💪", "运动", "这是运动的好时机，可以进行力量训练"));
                tips.add(createTip("🥗", "营养", "多吃新鲜蔬果，补充维生素和矿物质"));
                tips.add(createTip("📚", "学习", "精力充沛，适合学习新技能或制定计划"));
                tips.add(createTip("💡", "创意", "思维活跃，是发挥创造力的好时期"));
                break;
            case "ovulation":
                tips.add(createTip("💃", "社交", "魅力值最高的时期，适合社交活动"));
                tips.add(createTip("🏃‍♀️", "有氧运动", "可以进行有氧运动，如跑步、游泳"));
                tips.add(createTip("💕", "亲密关系", "是增进感情的好时机"));
                tips.add(createTip("🌸", "护肤", "皮肤状态较好，适合尝试新的护肤产品"));
                break;
            case "luteal":
                tips.add(createTip("🍫", "控制食欲", "可能会有食欲增加，注意控制糖分摄入"));
                tips.add(createTip("😌", "情绪管理", "可能情绪波动较大，多做放松活动"));
                tips.add(createTip("💧", "补水", "多喝水，减少水肿和不适"));
                tips.add(createTip("🛁", "自我关怀", "给自己更多关爱，泡个热水澡放松"));
                break;
            default:
                tips.add(createTip("📱", "记录", "坚持记录生理期数据，有助于更好地了解自己"));
                tips.add(createTip("🏥", "健康", "如有异常情况，及时咨询医生"));
        }
        
        return tips;
    }
    
    @Override
    public Map<String, Object> getCycleStatistics(Long userId) {
        List<PeriodRecord> records = periodRecordService.findRecentByUserId(userId, 12);
        Map<String, Object> statistics = new HashMap<>();
        
        statistics.put("totalRecords", records.size());
        statistics.put("averageCycleLength", calculateAverageCycleLength(userId));
        statistics.put("averagePeriodLength", calculateAveragePeriodLength(userId));
        
        if (!records.isEmpty()) {
            statistics.put("lastPeriodDate", records.get(0).getStartDate());
            statistics.put("nextPredictedDate", predictNextPeriodStart(userId));
        }
        Map<String, Object> anomalies = detectCycleAnomalies(userId);
        boolean isRegular = (Boolean) anomalies.getOrDefault("isRegular", false);
        statistics.put("regularityScore", isRegular ? 90 : 60);
        
        return statistics;
    }
    
    @Override
    public Map<String, List<LocalDate>> predictFutureCycles(Long userId, int cycles) {
        Map<String, List<LocalDate>> predictions = new HashMap<>();
        List<LocalDate> periodDates = new ArrayList<>();
        List<LocalDate> ovulationDates = new ArrayList<>();
        List<LocalDate> fertileDates = new ArrayList<>();
        
        LocalDate nextPeriodStart = predictNextPeriodStart(userId);
        if (nextPeriodStart == null) {
            return predictions;
        }
        
        Double avgCycleLength = calculateAverageCycleLength(userId);
        Double avgPeriodLength = calculateAveragePeriodLength(userId);
        
        if (avgCycleLength == null) avgCycleLength = (double) DEFAULT_CYCLE_LENGTH;
        if (avgPeriodLength == null) avgPeriodLength = (double) DEFAULT_PERIOD_LENGTH;
        
        for (int i = 0; i < cycles; i++) {
            LocalDate cycleStart = nextPeriodStart.plusDays((long) (avgCycleLength * i));
            for (int j = 0; j < avgPeriodLength.intValue(); j++) {
                periodDates.add(cycleStart.plusDays(j));
            }
            LocalDate ovulationDate = cycleStart.plusDays(avgCycleLength.longValue() - OVULATION_DAY);
            ovulationDates.add(ovulationDate);
            for (int k = -FERTILE_RANGE_BEFORE; k <= FERTILE_RANGE_AFTER; k++) {
                fertileDates.add(ovulationDate.plusDays(k));
            }
        }
        
        predictions.put("periodDates", periodDates);
        predictions.put("ovulationDates", ovulationDates);
        predictions.put("fertileDates", fertileDates);
        
        return predictions;
    }
    
    @Override
    public Map<String, Object> getCurrentPhase(Long userId) {
        List<PeriodRecord> records = periodRecordService.findRecentByUserId(userId, 1);
        Map<String, Object> phaseInfo = new HashMap<>();
        
        if (records.isEmpty()) {
            phaseInfo.put("phase", "unknown");
            phaseInfo.put("dayInCycle", 0);
            return phaseInfo;
        }
        
        PeriodRecord lastRecord = records.get(0);
        LocalDate today = LocalDate.now();
        long daysSinceLastPeriod = ChronoUnit.DAYS.between(lastRecord.getStartDate(), today);
        
        Double avgCycleLength = calculateAverageCycleLength(userId);
        if (avgCycleLength == null) avgCycleLength = (double) DEFAULT_CYCLE_LENGTH;
        
        String phase;
        if (daysSinceLastPeriod < DEFAULT_PERIOD_LENGTH) {
            phase = "menstrual";
        } else if (daysSinceLastPeriod >= avgCycleLength - 16 && daysSinceLastPeriod <= avgCycleLength - 12) {
            phase = "ovulation";
        } else if (daysSinceLastPeriod > avgCycleLength - 12) {
            phase = "luteal";
        } else {
            phase = "follicular";
        }
        
        phaseInfo.put("phase", phase);
        phaseInfo.put("dayInCycle", daysSinceLastPeriod + 1);
        phaseInfo.put("cycleLength", avgCycleLength);
        phaseInfo.put("daysUntilNext", avgCycleLength.longValue() - daysSinceLastPeriod);
        
        return phaseInfo;
    }
    
    /**
     * 获取周期异常列表
     * @param userId 用户ID
     * @return 周期异常列表
     */
    @Override
    public List<Map<String, Object>> getCycleAnomalies(Long userId) {
        List<Map<String, Object>> anomalies = new ArrayList<>();
        try {
            Map<String, Object> anomalyData = detectCycleAnomalies(userId);
            if ((Boolean) anomalyData.getOrDefault("hasEnoughData", false)) {
                @SuppressWarnings("unchecked")
                List<String> warnings = (List<String>) anomalyData.get("warnings");
                for (String warning : warnings) {
                    Map<String, Object> anomaly = new HashMap<>();
                    anomaly.put("type", "warning");
                    anomaly.put("message", warning);
                    anomaly.put("severity", "medium");
                    anomalies.add(anomaly);
                }
            } else {
                Map<String, Object> anomaly = new HashMap<>();
                anomaly.put("type", "info");
                anomaly.put("message", "数据不足，需要更多记录来检测异常");
                anomaly.put("severity", "low");
                anomalies.add(anomaly);
            }
        } catch (Exception e) {
            log.error("获取周期异常失败", e);
            Map<String, Object> anomaly = new HashMap<>();
            anomaly.put("type", "error");
            anomaly.put("message", "获取周期异常失败: " + e.getMessage());
            anomaly.put("severity", "high");
            anomalies.add(anomaly);
        }
        return anomalies;
    }
    
    /**
     * 获取预测信息
     * @param userId 用户ID
     * @return 预测信息
     */
    @Override
    public Map<String, Object> getPredictions(Long userId) {
        Map<String, Object> predictions = new HashMap<>();
        try {
            LocalDate nextPeriod = predictNextPeriodStart(userId);
            LocalDate ovulation = predictOvulationDate(userId);
            Map<String, LocalDate> fertilePeriod = predictFertilePeriod(userId);
            Map<String, Object> currentPhase = getCurrentPhase(userId);
            
            predictions.put("nextPeriodStart", nextPeriod);
            predictions.put("ovulationDate", ovulation);
            predictions.put("fertilePeriod", fertilePeriod);
            predictions.put("currentPhase", currentPhase);
            predictions.put("averageCycleLength", calculateAverageCycleLength(userId));
            predictions.put("averagePeriodLength", calculateAveragePeriodLength(userId));
        } catch (Exception e) {
            log.error("获取预测信息失败", e);
            predictions.put("error", "获取预测信息失败: " + e.getMessage());
        }
        return predictions;
    }
    
    /**
     * 获取未来周期预测
     * @param userId 用户ID
     * @param cycles 预测周期数
     * @return 未来周期预测列表
     */
    @Override
    public List<Map<String, Object>> getFutureCycles(Long userId, int cycles) {
        List<Map<String, Object>> futureCycles = new ArrayList<>();
        try {
            Map<String, List<LocalDate>> predictions = predictFutureCycles(userId, cycles);
            List<LocalDate> periodStarts = predictions.get("periodStarts");
            List<LocalDate> ovulationDates = predictions.get("ovulationDates");
            
            for (int i = 0; i < Math.min(periodStarts.size(), cycles); i++) {
                Map<String, Object> cycle = new HashMap<>();
                cycle.put("cycleNumber", i + 1);
                cycle.put("periodStart", periodStarts.get(i));
                if (i < ovulationDates.size()) {
                    cycle.put("ovulationDate", ovulationDates.get(i));
                }
                cycle.put("periodEnd", periodStarts.get(i).plusDays(DEFAULT_PERIOD_LENGTH - 1));
                futureCycles.add(cycle);
            }
        } catch (Exception e) {
            log.error("获取未来周期预测失败", e);
            Map<String, Object> errorCycle = new HashMap<>();
            errorCycle.put("error", "获取未来周期预测失败: " + e.getMessage());
            futureCycles.add(errorCycle);
        }
        return futureCycles;
    }
    
    private Map<String, String> createTip(String icon, String title, String content) {
        Map<String, String> tip = new HashMap<>();
        tip.put("icon", icon);
        tip.put("title", title);
        tip.put("content", content);
        return tip;
    }
    
    private String getMostCommonSymptom(Map<String, Integer> symptomFrequency) {
        return symptomFrequency.entrySet().stream()
                .max(Map.Entry.comparingByValue())
                .map(Map.Entry::getKey)
                .orElse("无");
    }
}
